﻿/*
	Copyright (C) 2019 Zongming Liu <1849059316@qq.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "Application.h"
#include "../base/GameWorld.h"
#include <cassert>
#include "ApplicationView.h"
#include "../base/Debuger.h"
#include  < MMSystem.h >
#include "../render/_Render.h"
#include "../base/InputEventDispatcher.h"
#pragma comment(lib, "winmm.lib")

namespace SkyEngine2d
{

	IApplication* Application::getInstance()
	{
		static Application a; return &a;
	}

	int Application::run()
	{
		//1.初始化应用程序
		if (!initApp())
		{
			return -1;
		}

		//2.开始游戏循环
		MSG msg = {};
		LARGE_INTEGER last;
		QueryPerformanceCounter(&last);
		LARGE_INTEGER now;
		int64_t interval = 0;
#if _DEBUG
		static float frame_cnt = 0;
		static int64_t last_frame_state = 0;
#endif

		timeBeginPeriod(1);

		while (WM_QUIT != msg.message)
		{
			if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			else
			{
				QueryPerformanceCounter(&now);

				interval = now.QuadPart - last.QuadPart;
				if (interval > m_animationInterval.QuadPart)
				{
					last.QuadPart = now.QuadPart;
					GameWorld::getInstance()->mainLoop(interval / (double)m_perSecondCount);
				}
				else
				{
					int64_t wait = (m_animationInterval.QuadPart - interval) * 1000LL / m_perSecondCount - 3L;
					if (wait > 1)
					{
						Sleep((DWORD)wait);
					}

				}
			}
		}

		timeEndPeriod(1);

		//3.退出游戏 清理
		this->clearApp();

		return 0;
	}

	void Application::setPerFrameInterval(double interval)
	{
		m_animationInterval.QuadPart = (LONGLONG)((interval * m_perSecondCount) + 0.5);
	}

	void Application::setAppDelegte(IAppDelegate * app_delegate)
	{
		m_appDelegate = app_delegate;
	}

	IApplicationView* Application::getAppView() const
	{
		return m_applicationView;
	}


	void Application::setWinMsgFilter(const WinMsgFilter & filter)
	{
		m_filter = filter;
	}


	Application::Application()
		:m_applicationView(nullptr)
		, m_appDelegate(nullptr)
		, m_inputEventDispater(nullptr)
		, m_perSecondCount(0)
		, m_isMinWindows(false)
		, m_gameWorld(nullptr)
	{

	}

	Application::~Application()
	{

	}

	const std::wstring& Application::getAppDir()
	{
		static std::wstring appDir;
		if (appDir.empty())
		{
			wchar_t buffer[MAX_PATH];
			if (!GetModuleFileName(nullptr, buffer, MAX_PATH))
			{
				throw "获取程序路径失败";
				return appDir;
			}
			appDir = buffer;
			auto pos = appDir.find_last_of(L"\\");
			appDir = appDir.substr(0, pos);
		}

		return appDir;
	}

	bool Application::initApp()
	{
		assert(m_appDelegate != NULL && "应用程序代理为空");
		if (!m_appDelegate)
		{
			return false;
		}

		//初始化滴答计数
		LARGE_INTEGER freq;
		QueryPerformanceFrequency(&freq);
		m_perSecondCount = freq.QuadPart;
		this->setPerFrameInterval(1 / 30.0f);

		// 初始化视图 
		m_applicationView = new ApplicationView;
		if (!m_applicationView->init(this, m_appDelegate->getWindowsSize()))
		{
			return false;
		}

		//初始化游戏世界
		auto render = m_applicationView->getRender();
		DebugerAssert(render->isInit(), "渲染没有初始化");

		m_gameWorld = new GameWorld;
		m_gameWorld->init(render, render, m_applicationView, this);

		//初始化事件分发
		m_inputEventDispater = InputEventDispatcher::getInstance();

		//代理初始化 
		if (!m_appDelegate->appInit())
		{
			return false;
		}

		return true;
	}

	void Application::clearApp()
	{
		GameWorld::getInstance()->end();
		delete m_gameWorld;
		delete m_applicationView;

		m_gameWorld = nullptr;
		m_applicationView = nullptr;
	}

	LRESULT Application::processMsg(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
		//wondow消息过滤
		if (this->m_filter)
		{
			if (this->m_filter(hwnd, msg, wParam, lParam))
			{
				return 0;
			}
		}

		// 默认的引擎处理
		PAINTSTRUCT paintStruct;
		HDC hDC;
		switch (msg)
		{
		case WM_CREATE:
		{
			//窗口居中显示

			RECT rc, rcDlg, desktop_rect;
			HWND hwnd_desktop = GetDesktopWindow();

			GetWindowRect(hwnd_desktop, &desktop_rect);
			GetWindowRect(hwnd, &rcDlg);
			CopyRect(&rc, &desktop_rect);

			OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
			OffsetRect(&rc, -rc.left, -rc.top);
			OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);

			SetWindowPos(hwnd, HWND_TOP,
				desktop_rect.left + (rc.right / 2),
				desktop_rect.top + (rc.bottom / 2),
				0, 0,
				SWP_NOSIZE);

			RECT viewRect;
			GetWindowRect(hwnd, &viewRect);
			m_applicationView->setWinPosition(Vector2(viewRect.left, viewRect.top));
		}
		break;
		case WM_SIZE:
		{
			if (wParam == SIZE_MINIMIZED)
			{
				m_isMinWindows = true;
				m_appDelegate->appEnterBackground();
			}
			else
			{
				if (m_applicationView)
				{
					m_applicationView->onResize(hwnd, LOWORD(lParam), HIWORD(lParam));
				}
			}
			break;
		}
		case WM_PAINT:
		{
			if (m_isMinWindows)
			{
				m_appDelegate->appEnterForeground();
				m_isMinWindows = false;
			}
			hDC = BeginPaint(hwnd, &paintStruct);
			EndPaint(hwnd, &paintStruct);
			break;
		}
		case WM_RBUTTONDOWN:
		case WM_LBUTTONDOWN:
		case WM_RBUTTONUP:
		case WM_LBUTTONUP:
		case WM_MOUSEMOVE:
		case WM_LBUTTONDBLCLK:
		case WM_RBUTTONDBLCLK:
		{
			m_inputEventDispater->dispatchMouseEvent(hwnd, msg, LOWORD(lParam), m_applicationView->getWinSize().height - HIWORD(lParam));
			break;
		}

		case WM_KEYDOWN:
		case WM_KEYUP:
		{
			m_inputEventDispater->dispatchKeyEvent(hwnd, msg, wParam);
			break;
		}
		case WM_CHAR:
		case WM_IME_CHAR:
		{
			m_inputEventDispater->dispatchCharterEvent(wParam);
			break;
		}
		case WM_DESTROY:
		{
			PostQuitMessage(0);
			break;
		}
		default:
			return ::DefWindowProc(hwnd, msg, wParam, lParam);
		}
		return 0;
	}

}
